/**
 * author : zhengjiachao
 * 2018年4月23日
 * 
 * 合并规则：
 *  只支持列表合并，不支持树形合并。树形合并太复杂，暂时不支持。通过把表单组件封装成单个的标签，来实现列表化。
 *  1. 查找origin和designed里面的所有bill-root-name属性的bForm节点，属性的名字能对应上的，就是会互相合并的内容。
 *     例如bill-root-name="a" 和 bill-root-name=“a”的就会合并。 解析多个合并结构时，会递归origin和designed,找到一个bill-root-name时，因为是列表结构，所以就不再递归当前节点的子节点。不会处理嵌套情况，因为不允许存在
 *  2. 支持多段bill-root-name分别合并。
 *  3. 每个field都有一个field-meta-id属性，合并时，通过field-meta-id进行对应
 *  4. origin中能对应上designed中field-meta-id的，就按照designed中的为准（origin中的field上加了field-meta-const的除外，加了此属性的，默认为不可修改，按照origin的为准）。
 *  5. designed中的field配置了field-meta-deleted属性的，在最终结果中删除此field（在designed中配置属性，在合并时删除，是为了区分是原有属性被删除了，还是新增的系统属性）
 *  6. origin中对应不上的field，就直接加到结果的最后面
 *  7. 合并区域之外的内容，以designed为准
 */

var convert = require('xml-js');
let mergePart = function(origin, designed){
    let originElements = origin.elements;
    let designedElements = designed.elements;
    //原始元素的map，用来减少后面合并时的对比元素定位
    let originMap = {};
    originElements.forEach(element => {
        let id = element['attributes']['field-meta-id'];
        if(id){
            originMap[id] = element;
        }
    });
    //倒序处理所有的目标元素，方便删除
    for (let i = designedElements.length - 1; i >= 0 ; i--) {
        const resultItem = designedElements[i];
        let originItem = originMap[resultItem['attributes']['field-meta-id']];
        if(originItem){
            //如果目标元素对应的原始元素设置了field-meta-const属性，那么使用原始元素覆盖结果元素map中对象的属性（覆盖属性可以省去到数组里面替换对象的麻烦）
            //设置了field-meta-const属性的必须要用原来的元素进行覆盖，以避免运行时新版本中调整了field-meta-const属性，而设计器没有调整的问题，也可以避免人为修改设计结果导致属性被改动的问题
            if(originItem['attributes']['field-meta-const']){
                Object.assign(resultItem, originItem);
            }else{
                //如果目标元素的元素内描述了field-meta-deleted属性，那么从结果中把对应的元素删除
                if(resultItem['attributes']['field-meta-deleted']){
                    designedElements.splice(i,1);
                }
            }
            //把能对应上的原始元素从originMap里面删除
            originMap[resultItem['attributes']['field-meta-id']] = null;
        }

    }
    //遍历剩下的originMap中的元素，全部添加到结果数组的结尾
    for (let i = originElements.length - 1; i >= 0 ; i--) {
        const e = originElements[i];
        if(!originMap[e['attributes']['field-meta-id']]){
            originElements.splice(i,1);
        }else{
            designedElements.push(e);
        }
    }

};


/**
 * 从节点中解析出所有的合并区域对象，并缓存起来
 */
let getNamedBlocks = function(node){
    let blocks = {};
    let deepTraversal = function(node) {  
        let name = node['attributes'] ? node['attributes']['bill-root-name'] : null;
        if(name){
            blocks[name] = node;
        }else{
            let children = node.elements;    
            if(children){
                for (let i = 0; i < children.length; i++){
                    deepTraversal(children[i]);    
                }    
            }
        }
    };
    deepTraversal(node);
    return blocks;
};

const tool = {
    merge: function(originXML, designedXML, callback){
        let config = {compact: false, spaces: 4};
        let origin = JSON.parse(convert.xml2json(originXML, config));
        let designed = JSON.parse(convert.xml2json(designedXML, config));

        let originBlocks = getNamedBlocks(origin);
        let designedBlocks = getNamedBlocks(designed);

        for (const blockName in designedBlocks) {
            if (designedBlocks.hasOwnProperty(blockName)) {
                const designedBlock = designedBlocks[blockName];
                const originBlock = originBlocks[blockName];
                //如果查找到了对应的原始块，那么就合并，合并后，直接覆盖原来的designedBlock内容
                if(originBlock){
                    mergePart(originBlock, designedBlock);
                }
            }
        }
        callback(convert.json2xml(designed, config));
    }
};

export default tool;